package com.doyled.jump.object;

import java.awt.Graphics;
import java.awt.Point;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;

import javax.jws.soap.SOAPBinding.Use;

import com.doyled.jump.IGame.GameInterface;
import com.doyled.jump.animation.Animation;
import com.doyled.jump.animation.GapTimeAnimation;
import com.doyled.jump.animation.TextAnimation_;
import com.doyled.jump.framework.GameMouseLocation;
import com.doyled.jump.user.Player;
import com.doyled.jump.user.PlayerAI;
import com.doyled.jump.user.User;
import com.doyled.jump.util.ChessKind;
import com.doyled.jump.util.GamePhrase;
import com.doyled.jump.util.HorseState;
import com.doyled.jump.util.PlayerMode;
import com.doyled.jump.util.SpotKind;
import com.doyled.jump.util.TeamInfo;
import com.doyled.jump.util.UserSetting;

public class Environment implements GameInterface {

	private final int totalSpots = 48;
	private final int redStart = 0;
	private final int blueStart = 12;
	private final int yellowStart = 24;
	private final int greenStart = 36;

	GameMouseLocation mouseLocation;

	Spot redFarmSpot, blueFarmSpot, yellowFarmSpot, greenFarmSpot; // spot of
																	// team
																	// farms
	LinkedList<Spot> spots;
	LinkedList<Spot> redDes;
	LinkedList<Spot> blueDes;
	LinkedList<Spot> yellowDes;
	LinkedList<Spot> greenDes;
	Horsemen[] horses;
	TeamInfo redTeam;
	TeamInfo blueTeam;
	TeamInfo yellowTeam;
	TeamInfo greenTeam;
	Horsemen[] redHorses;
	Horsemen[] blueHorses;
	Horsemen[] yellowHorses;
	Horsemen[] greenHorses;

	Dice dice;

	LinkedList<User> players;
	int turn; // turn in game: the number from 0 to 3
	GamePhrase phrase; // Phrases in game
	boolean changePhrase; // the flag for changeing phrase
	LinkedList<Animation> animations; // the animations that are animating in
										// game
	InfoPanel infoPanel;

	public Environment(GameMouseLocation mouseLocation) {
		this.mouseLocation = mouseLocation;
		dice = new Dice();
		this.animations = new LinkedList<Animation>();
		this.infoPanel = new InfoPanel(350, 25);
		spotsInit();
		horsesInit();
		createTeamInfo();
		createPlayer();
		turnInit();
	}

	/**
	 * Load spots from spots file
	 */
	private void spotsInit() {
		// init spots
		spots = new LinkedList<>();
		redDes = new LinkedList<Spot>();
		blueDes = new LinkedList<Spot>();
		yellowDes = new LinkedList<Spot>();
		greenDes = new LinkedList<Spot>();
		// read spots
		BufferedReader reader = new BufferedReader(new InputStreamReader(this
				.getClass().getResourceAsStream("/Spots/Spots.txt")));
		String line = "";
		int id = 0;
		// read spots
		try {
			while ((line = reader.readLine()) != null) {
				String[] pieces = line.split("\t");
				int x = Integer.parseInt(pieces[1]);
				int y = Integer.parseInt(pieces[2]);
				if (pieces[0].equals("0")) { // create farm spots
					int kind = Integer.parseInt(pieces[3]);
					switch (kind) {
					case 1:
						redFarmSpot = new Spot(x, y, ChessKind.Red,
								SpotKind.FarmSpot);
						break;
					case 2:
						blueFarmSpot = new Spot(x, y, ChessKind.Blue,
								SpotKind.FarmSpot);
						break;
					case 3:
						yellowFarmSpot = new Spot(x, y, ChessKind.Yellow,
								SpotKind.FarmSpot);
						break;
					default:
						greenFarmSpot = new Spot(x, y, ChessKind.Green,
								SpotKind.FarmSpot);
						break;
					}
				} else if (pieces[0].equals("2")) { // create destination spots
					int kind = Integer.parseInt(pieces[3]);
					int index = 0;
					switch (kind) {
					case 1:
						index = redDes.size() + 1;
						redDes.add(new Spot(index, x, y, ChessKind.None,
								SpotKind.DestinationSpot));
						break;
					case 2:
						index = blueDes.size() + 1;
						blueDes.add(new Spot(index, x, y, ChessKind.None,
								SpotKind.DestinationSpot));
						break;
					case 3:
						index = yellowDes.size() + 1;
						yellowDes.add(new Spot(index, x, y, ChessKind.None,
								SpotKind.DestinationSpot));
						break;
					default:
						index = greenDes.size() + 1;
						greenDes.add(new Spot(index, x, y, ChessKind.None,
								SpotKind.DestinationSpot));
						break;
					}
				} else { // create spots
					Spot tempSpot = new Spot(++id, x, y, ChessKind.None,
							SpotKind.RoadSpot);
					spots.add(tempSpot);
				}
			}
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Create horses for game
	 */
	private void horsesInit() {
		int offsetInFarm = 20;
		horses = new Horsemen[] {
				new Horsemen(ChessKind.Red, new Spot(redFarmSpot.getX()
						- offsetInFarm, redFarmSpot.getY() - offsetInFarm,
						ChessKind.Red, SpotKind.FarmSpot)),
				new Horsemen(ChessKind.Red, new Spot(redFarmSpot.getX()
						+ offsetInFarm, redFarmSpot.getY() - offsetInFarm,
						ChessKind.Red, SpotKind.FarmSpot)),
				new Horsemen(ChessKind.Red, new Spot(redFarmSpot.getX()
						- offsetInFarm, redFarmSpot.getY() + offsetInFarm,
						ChessKind.Red, SpotKind.FarmSpot)),
				new Horsemen(ChessKind.Red, new Spot(redFarmSpot.getX()
						+ offsetInFarm, redFarmSpot.getY() + offsetInFarm,
						ChessKind.Red, SpotKind.FarmSpot)),

				new Horsemen(ChessKind.Blue, new Spot(blueFarmSpot.getX()
						- offsetInFarm, blueFarmSpot.getY() - offsetInFarm,
						ChessKind.Blue, SpotKind.FarmSpot)),
				new Horsemen(ChessKind.Blue, new Spot(blueFarmSpot.getX()
						+ offsetInFarm, blueFarmSpot.getY() - offsetInFarm,
						ChessKind.Blue, SpotKind.FarmSpot)),
				new Horsemen(ChessKind.Blue, new Spot(blueFarmSpot.getX()
						- offsetInFarm, blueFarmSpot.getY() + offsetInFarm,
						ChessKind.Blue, SpotKind.FarmSpot)),
				new Horsemen(ChessKind.Blue, new Spot(blueFarmSpot.getX()
						+ offsetInFarm, blueFarmSpot.getY() + offsetInFarm,
						ChessKind.Blue, SpotKind.FarmSpot)),

				new Horsemen(ChessKind.Yellow, new Spot(yellowFarmSpot.getX()
						- offsetInFarm, yellowFarmSpot.getY() - offsetInFarm,
						ChessKind.Yellow, SpotKind.FarmSpot)),
				new Horsemen(ChessKind.Yellow, new Spot(yellowFarmSpot.getX()
						+ offsetInFarm, yellowFarmSpot.getY() - offsetInFarm,
						ChessKind.Yellow, SpotKind.FarmSpot)),
				new Horsemen(ChessKind.Yellow, new Spot(yellowFarmSpot.getX()
						- offsetInFarm, yellowFarmSpot.getY() + offsetInFarm,
						ChessKind.Yellow, SpotKind.FarmSpot)),
				new Horsemen(ChessKind.Yellow, new Spot(yellowFarmSpot.getX()
						+ offsetInFarm, yellowFarmSpot.getY() + offsetInFarm,
						ChessKind.Yellow, SpotKind.FarmSpot)),

				new Horsemen(ChessKind.Green, new Spot(greenFarmSpot.getX()
						- offsetInFarm, greenFarmSpot.getY() - offsetInFarm,
						ChessKind.Green, SpotKind.FarmSpot)),
				new Horsemen(ChessKind.Green, new Spot(greenFarmSpot.getX()
						+ offsetInFarm, greenFarmSpot.getY() - offsetInFarm,
						ChessKind.Green, SpotKind.FarmSpot)),
				new Horsemen(ChessKind.Green, new Spot(greenFarmSpot.getX()
						- offsetInFarm, greenFarmSpot.getY() + offsetInFarm,
						ChessKind.Green, SpotKind.FarmSpot)),
				new Horsemen(ChessKind.Green, new Spot(greenFarmSpot.getX()
						+ offsetInFarm, greenFarmSpot.getY() + offsetInFarm,
						ChessKind.Green, SpotKind.FarmSpot)), };
		redHorses = new Horsemen[] { horses[0], horses[1], horses[2], horses[3] };
		blueHorses = new Horsemen[] { horses[4], horses[5], horses[6],
				horses[7] };
		yellowHorses = new Horsemen[] { horses[8], horses[9], horses[10],
				horses[11] };
		greenHorses = new Horsemen[] { horses[12], horses[13], horses[14],
				horses[15] };
	}

	private Horsemen[] getHorses(ChessKind kind){
		switch (kind) {
		case Red:
			return redHorses;
		case Blue:
			return blueHorses;
		case Yellow:
			return yellowHorses;
		case Green:
			return greenHorses;

		default:
			return null;
		}
	}
	
	private void visibleHorse(Horsemen[] horses){
		for(Horsemen horse : horses){
			horse.setVisible(true);
		}
	}
	
	/**
	 * Create team infos for all team in game
	 */
	private void createTeamInfo() {
		redTeam = new TeamInfo(ChessKind.Red, redHorses,
				getStartSpot(ChessKind.Red), getLastSpotInRoad(ChessKind.Red));
		blueTeam = new TeamInfo(ChessKind.Blue, blueHorses,
				getStartSpot(ChessKind.Blue), getLastSpotInRoad(ChessKind.Blue));
		yellowTeam = new TeamInfo(ChessKind.Yellow, yellowHorses,
				getStartSpot(ChessKind.Yellow),
				getLastSpotInRoad(ChessKind.Yellow));
		greenTeam = new TeamInfo(ChessKind.Green, greenHorses,
				getStartSpot(ChessKind.Green),
				getLastSpotInRoad(ChessKind.Green));
		// add to team info
		redTeam.setTeamDes(redDes);
		blueTeam.setTeamDes(blueDes);
		yellowTeam.setTeamDes(yellowDes);
		greenTeam.setTeamDes(greenDes);
	}

	private TeamInfo getTeamInfo(ChessKind kind) {
		TeamInfo teamInfo = redTeam;
		switch (kind) {
		case Red:
			teamInfo = redTeam;
			break;

		case Blue:
			teamInfo = blueTeam;
			break;

		case Yellow:
			teamInfo = yellowTeam;
			break;

		case Green:
			teamInfo = greenTeam;
			break;
		}
		return teamInfo;
	}

	/**
	 * Create players for game
	 */
	private void createPlayer() {
		players = new LinkedList<User>();
		List<User> temp = new LinkedList<User>();

		if (UserSetting.mode == PlayerMode.Single) {	//single player mode
			ChessKind playerChessKind = ChessKind
					.fromInteger(UserSetting.playerColor);
			TeamInfo playerInfo = getTeamInfo(playerChessKind);
			playerInfo.setName(UserSetting.playerName);
			Player player = new Player(this, playerChessKind,
					playerInfo);
			visibleHorse(getHorses(playerChessKind)); 	//visible player horses
			temp.add(player);

			// add ai player
			for (int i = 0; i <= 3; i++) {
				if (temp.size() != UserSetting.numberOfPlayers) {
					if (i != UserSetting.playerColor) {
						ChessKind chessKind = ChessKind.fromInteger(i);
						PlayerAI playerAI = new PlayerAI(this, chessKind,
								getTeamInfo(chessKind));
						visibleHorse(getHorses(chessKind)); 	//visible player AI horses
						temp.add(playerAI);
					}
				}
			}
		} else if (UserSetting.mode == PlayerMode.two_Player) {	//two player mode
			ChessKind player1ChessKind = ChessKind
					.fromInteger(UserSetting.player1Color);
			TeamInfo player1Info = getTeamInfo(player1ChessKind);
			player1Info.setName(UserSetting.player1Name);
			Player player1 = new Player(this, player1ChessKind,
					player1Info);
			visibleHorse(getHorses(player1ChessKind)); 	//visible player1 horses
			temp.add(player1);

			ChessKind player2ChessKind = ChessKind
					.fromInteger(UserSetting.player2Color);
			TeamInfo player2Info = getTeamInfo(player2ChessKind);
			player2Info.setName(UserSetting.player2Name);
			Player player2 = new Player(this, player2ChessKind,
					player2Info);
			visibleHorse(getHorses(player2ChessKind)); 	//visible player2 horses
			temp.add(player2);

			//add ai player
			for (int i = 0; i <= 3; i++) {
				if (temp.size() != UserSetting.numberOfPlayers) {
					if (i != UserSetting.player1Color
							&& i != UserSetting.player2Color) {
						ChessKind chessKind = ChessKind.fromInteger(i);
						PlayerAI playerAI = new PlayerAI(this, chessKind,
								getTeamInfo(chessKind));
						visibleHorse(getHorses(chessKind)); 	//visible player AI horses
						temp.add(playerAI);
					}
				}
			}
		}

		int expextedIndex = 0;
		for (int i = 0; i < 4; i++) {
			expextedIndex = i;
			for (User user : temp) {
				ChessKind kind = user.getKind();
				int index = ChessKind.toInteger(user.getKind());
				if (index == expextedIndex) {
					players.add(user);
				}
			}
		}
	}

	/**
	 * Initialize a game
	 */
	private void turnInit() {
		this.turn = 0;
		this.phrase = GamePhrase.ThrowingDice;
		this.changePhrase = true;
		infoPanel.setAnimationInfoText("Start", 2);
		this.animations.add(infoPanel.getLastAnimation());
	}

	/**
	 * Move to the next turn
	 */
	private void nextTurn() {
		turn = (turn + 1) % UserSetting.numberOfPlayers;

	}

	public int getDiceNumber() {
		return dice.getValue();
	}

	public Horsemen[] getAllHorses() {
		return horses;
	}

	/**
	 * change the number of horses in farm, road and des
	 * 
	 * @param kind
	 *            kind of team
	 * @param farm
	 *            number of horses in farm changed
	 * @param road
	 *            number of horses in road changed
	 * @param des
	 *            number of horses in destination changed
	 */
	private void changeTeamInfo(ChessKind kind, int farm, int road, int des) {
		TeamInfo teamInfo = null;
		switch (kind) {
		case Red:
			teamInfo = redTeam;
			System.out.print("Red team : ");
			break;
		case Blue:
			teamInfo = blueTeam;
			System.out.print("Blue team : ");
			break;
		case Yellow:
			teamInfo = yellowTeam;
			System.out.print("Yellow team : ");
			break;
		case Green:
			teamInfo = greenTeam;
			System.out.print("Green team : ");
			break;
		}
		teamInfo.setHorsesInFarm(teamInfo.getHorsesInFarm() + farm);
		teamInfo.setHorsesOnRoad(teamInfo.getHorsesOnRoad() + road);
		teamInfo.setHorsesInDestination(teamInfo.getHorsesInDestination() + des);
		System.out.println(teamInfo.getHorsesInFarm() + " : "
				+ teamInfo.getHorsesOnRoad() + " : "
				+ teamInfo.getHorsesInDestination());
	}

	/**
	 * Request environment that user want to release a horse from his/her farm
	 * 
	 * @param kind
	 * @return the released horse, return null if no horse can release
	 */
	public Horsemen releaseHorse(ChessKind kind) {
		Horsemen releasedHorse = null;
		if (dice.getValue() == 1 || dice.getValue() == 6) {
			switch (kind) {
			case Red:
				releasedHorse = releaseHorse(redHorses, redStart);
				break;
			case Blue:
				releasedHorse = releaseHorse(blueHorses, blueStart);
				break;
			case Yellow:
				releasedHorse = releaseHorse(yellowHorses, yellowStart);
				break;
			case Green:
				releasedHorse = releaseHorse(greenHorses, greenStart);
				break;
			}
		}
		endPhrase();
		return releasedHorse;
	}

	/**
	 * Put a horse in farm to the starting point
	 * 
	 * @param horses
	 * @param startSpot
	 * @return the released horse, return null if no horse can release
	 */
	private Horsemen releaseHorse(Horsemen[] horses, int startSpotId) {
		Spot startSpot = spots.get(startSpotId);
		if (startSpot.getOccupiedChessKind() != horses[0].getKind()) { // only
																		// can
																		// release
																		// horse
																		// when
																		// start
																		// spot
																		// wasn't
																		// occupied
																		// by
																		// another
																		// team
																		// horse
			for (Horsemen horse : horses) {
				if (horse.getState() == HorseState.InFarm) {
					releaseSpot(horse.getSpot()); // release current spot
					occupySpot(startSpot, horse); // occupy start spot
					horse.setState(HorseState.InRoad);
					changeTeamInfo(horse.getKind(), -1, 1, 0);
					return horse;
				}
			}
		}
		return null;
	}

	/**
	 * Release a spot
	 * 
	 * @param spot
	 */
	private void releaseSpot(Spot spot) {
		spot.setOccupiedChessKind(ChessKind.None);
		spot.setOccupied(false);
		spot.setOccupiedHorse(null);
	}

	/**
	 * Occupy a spot by a horse
	 * 
	 * @param spot
	 * @param horse
	 */
	private void occupySpot(Spot spot, Horsemen horse) {
		if (spot.getOccupiedChessKind() != horse.getKind()
				&& spot.getOccupiedHorse() != null) { // spot is occupied by
														// enemy horse
			kickEnemyHorse(spot);
		}
		spot.setOccupied(true);
		spot.setOccupiedChessKind(horse.getKind());
		spot.setOccupiedHorse(horse);
		horse.setSpot(spot);
	}

	private void kickEnemyHorse(Spot spot) {
		Horsemen enemyHorse = spot.getOccupiedHorse();
		enemyHorse.beKickedToFarm();
		spot.setOccupied(false);
		spot.setOccupiedChessKind(ChessKind.None);
		spot.setOccupiedHorse(null);
		changeTeamInfo(enemyHorse.getKind(), 1, -1, 0);// change team info of
														// enemy team
	}

	public List<Spot> getRoadSpots() {
		return spots;
	}

	/*
	 * move a horse (will be changed then)
	 */
	public LinkedList<Spot> getMovableSpots(Horsemen horse) {
		LinkedList<Spot> jumpingSpots = new LinkedList<Spot>();
		if (horse == null)
			return jumpingSpots; // return empty list spot if horse is null

		ChessKind kind = horse.getKind();
		if (horse.getState() == HorseState.InRoad) { // only for horse in road
			int maxId = getLastSpotInRoad(horse.getKind()).getId();
			int oldId = horse.getSpot().getId();
			int newId = (oldId + dice.getValue()) % (totalSpots);
			newId = (newId == 0) ? totalSpots : newId;

			if (oldId == maxId) { // horse is in the gate spot
				jumpingSpots
						.addAll(getSpotsInDestination(dice.getValue(), kind));
			} else if ((kind == ChessKind.Red && oldId < newId && newId <= maxId)
					|| !(kind != ChessKind.Red && oldId <= maxId && newId > maxId)) { // horse
																						// is
				// in road
				// and don't
				// go pass
				// the gate
				// spot
				if (oldId > newId) {
					jumpingSpots.addAll(spots.subList(oldId, totalSpots));
					jumpingSpots.addAll(spots.subList(0, newId));
				} else {
					jumpingSpots.addAll(spots.subList(oldId, newId));
				}
			}

		} else if (horse.getState() == HorseState.InDestination) { // for horse
																	// which is
																	// in the
																	// destination
			List<Spot> destinationSpots = getSpotsInDestination(6, kind);
			if (!horse.getSpot().equals(destinationSpots.get(6 - 1))) {
				int diceValue = dice.getValue();
				int currentDesId = horse.getSpot().getId(); // get current spot
															// id
				if (diceValue == (currentDesId + 1)) {
					jumpingSpots.add(destinationSpots.get(diceValue - 1));
				}
			}
		}
		// check that there is any other horses which are in the movable road of
		// the horse
		if (jumpingSpots.size() != 0) { // horse haven't gotten to the last
										// destination
			boolean isPrevented = false;
			for (int i = 0; i < jumpingSpots.size() - 1; i++) { // except the
																// last spot
																// where the
																// horse can
																// attack
																// another horse
				if (jumpingSpots.get(i).isOccupied()) {
					isPrevented = true;
				}
			}
			if (jumpingSpots.get(jumpingSpots.size() - 1)
					.getOccupiedChessKind() == horse.getKind()) {
				isPrevented = true;
			}
			if (isPrevented)
				jumpingSpots.clear();
		}
		// return the movable spots
		return jumpingSpots;
	}

	/**
	 * show the movable spots of a specific horse
	 * 
	 * @param movableSpots
	 */
	public void showMovableSpots(List<Spot> movableSpots) {
		for (Spot spot : movableSpots) {
			spot.setVisble(true);
		}
	}

	/**
	 * hide the movable spots of a specific horse
	 * 
	 * @param movableSpots
	 */
	public void hideMovableSpots(List<Spot> movableSpots) {
		for (Spot spot : movableSpots) {
			spot.setVisble(false);
		}
	}

	/**
	 * Move horse
	 * 
	 * @param horse
	 * @param movableSpot
	 */
	public void moveHorse(Horsemen horse) {
		List<Spot> movableSpot = getMovableSpots(horse); // get movable spots of
															// the horse
		if (!movableSpot.isEmpty()) { // if the horse can move
			// horse before moving
			HorseState stateBefore = horse.getState();
			// move horse
			releaseSpot(horse.getSpot());
			int lastIndex = movableSpot.size() - 1;
			occupySpot(movableSpot.get(lastIndex), horse);
			if (horse.getSpot().getSpotKind() == SpotKind.DestinationSpot) { // set
																				// that
																				// the
																				// horse
																				// in
																				// destination
				horse.setState(HorseState.InDestination);
			}
			// horse after moving
			HorseState stateAfter = horse.getState();
			// change team infor
			if (stateBefore != stateAfter) {
				changeTeamInfo(horse.getKind(), 0, -1, 1);
			}
		}
		endPhrase();
	}

	/**
	 * Get the last spot in road before getting into the destination
	 * 
	 * @param kind
	 * @return
	 */
	private Spot getLastSpotInRoad(ChessKind kind) {
		int lastSpot = 0;
		switch (kind) {
		case Red:
			lastSpot = 47;
			break;
		case Blue:
			lastSpot = 11;
			break;
		case Yellow:
			lastSpot = 23;
			break;
		case Green:
			lastSpot = 35;
			break;
		}
		return spots.get(lastSpot);
	}

	/**
	 * Get spots in destination that the horse is about to pass
	 * 
	 * @param n
	 *            number of spots
	 * @param kind
	 *            kind of horse
	 * @return list of the spots
	 */
	private List<Spot> getSpotsInDestination(int n, ChessKind kind) {
		List<Spot> desSpots = null;
		switch (kind) {
		case Red:
			desSpots = redDes.subList(0, n);
			break;
		case Blue:
			desSpots = blueDes.subList(0, n);
			break;
		case Yellow:
			desSpots = yellowDes.subList(0, n);
			break;
		case Green:
			desSpots = greenDes.subList(0, n);
			break;
		}
		return desSpots;
	}

	/**
	 * Get the click point that the player have just clicked
	 * 
	 * @return clicked point
	 */
	public Point getClickPoint() {
		Point mouseClick = mouseLocation.getClickPosition();
		return mouseClick;
	}

	/**
	 * Check the player has just clicked in dice or not
	 * 
	 * @param mouseClick
	 *            point of clicked mouse
	 * @return true or false
	 */
	public boolean clickInDice(Point mouseClick) {
		if (mouseClick == null)
			return false;
		if (dice.isInDice((int) mouseClick.getX(), (int) mouseClick.getY())) {
			return true;
		}
		return false;
	}

	/**
	 * get horse that player have just clicked
	 * 
	 * @param mouseClick
	 * @param kind
	 *            kind of team
	 * @return
	 */
	public Horsemen clickInHorse(Point mouseClick, ChessKind kind) {
		if (mouseClick == null)
			return null;
		Horsemen clickedHorse = null;
		switch (kind) {
		case Red:
			clickedHorse = clickInTeamHorse(mouseClick, redHorses);
			break;
		case Blue:
			clickedHorse = clickInTeamHorse(mouseClick, blueHorses);
			break;
		case Yellow:
			clickedHorse = clickInTeamHorse(mouseClick, yellowHorses);
			break;
		case Green:
			clickedHorse = clickInTeamHorse(mouseClick, greenHorses);
			break;
		}
		return clickedHorse;
	}

	private Horsemen clickInTeamHorse(Point mouseClick, Horsemen[] horses) {
		for (Horsemen horse : horses) {
			if (horse.isOnHorse(mouseClick) == true) {
				return horse;
			}
		}
		return null;
	}

	/**
	 * get start spot of a team
	 * 
	 * @param kind
	 *            kind of team
	 * @return the start spot
	 */
	private Spot getStartSpot(ChessKind kind) {
		Spot startSpot = null;
		switch (kind) {
		case Red:
			startSpot = spots.get(redStart);
			break;
		case Blue:
			startSpot = spots.get(blueStart);
			break;
		case Yellow:
			startSpot = spots.get(yellowStart);
			break;
		case Green:
			startSpot = spots.get(greenStart);
			break;
		}
		return startSpot;
	}

	/**
	 * Check the player has just clicked the starting spot or not
	 * 
	 * @param mouseClick
	 *            point of clicked mouse
	 * @param kind
	 *            kind of team
	 * @return true or false
	 */
	public boolean clickInStartSpot(Point mouseClick, ChessKind kind) {
		if (mouseClick == null)
			return false;
		Spot startSpot = getStartSpot(kind);
		return startSpot.isInSpot(mouseClick);
	}

	/**
	 * get the spots that player have just clicked
	 * 
	 * @param mouseClick
	 * @return
	 */
	public Spot getClickedSpot(Point mouseClick) {
		if (mouseClick != null) {
			List<Spot> allSpots = getAllSpots();
			for (Spot spot : allSpots) {
				if (spot.isInSpot(mouseClick)) {
					return spot;
				}
			}
		}
		return null;
	}

	private List<Spot> getAllSpots() {
		LinkedList<Spot> allSpots = new LinkedList<Spot>();
		allSpots.addAll(spots);
		allSpots.addAll(redDes);
		allSpots.addAll(blueDes);
		allSpots.addAll(yellowDes);
		allSpots.addAll(greenDes);
		return allSpots;
	}

	/**
	 * Move to End Phrase
	 */
	public void endPhrase() {
		changePhrase = true;
		phrase = GamePhrase.values()[(phrase.ordinal() + 1) % 3];
		this.animations.add(new GapTimeAnimation(1));
	}

	private void setTurnText(int playerId) {
		User player = players.get(playerId);
		String name = player.getTeamInfo().getName();
		infoPanel.setTurnText(name + " : ");
	}

	private void setPhraseTextAnimation(GamePhrase phrase) {
		boolean result = false;
		switch (phrase) {
		case ThrowingDice:
			infoPanel.setStaticInfoText("Throw Dice");
			break;
		case Moving:
			result = infoPanel.setStaticInfoText("Moving");
			if (result)
				this.animations.add(new GapTimeAnimation(1));
			break;
		case End:
			result = infoPanel.setStaticInfoText("End Move");
			if (result)
				this.animations.add(new GapTimeAnimation(1));
			break;

		default:
			break;
		}
	}

	private User checkWin() {
		for (User player : players) {
			ChessKind kind = player.getKind();
			TeamInfo teamInfo = player.getTeamInfo();
			if (teamInfo.getHorsesInDestination() == 4) {
				// check all 4 horses are in the last spots or not
				List<Spot> teamDesSpots = teamInfo.getTeamDes(); // get team
																	// destination
																	// spots
				for (int i = 5; i <= 5 - 4; i++) {
					if (spots.get(i).getOccupiedChessKind() != kind) {
						return null;
					}
				}
				return player;
			}
		}
		return null;
	}

	/*
	 * update environment
	 */
	public void tick() {
		// update info panel
		infoPanel.tick();
		// update animation
		if (!animations.isEmpty()) {
			if (animations.getFirst().isAnimating() == false) {
				animations.removeFirst();
			} else {
				animations.getFirst().tick();
			}
			getClickPoint();
			return;
		}
		// check win
		User winner = checkWin();
		if (winner != null) {
			infoPanel.setStaticInfoText("is winner.");
		}

		// update phrases
		if (changePhrase == true) {
			User currentPlayer = players.get(turn);
			if (phrase == GamePhrase.ThrowingDice) {
				setTurnText(turn);
				setPhraseTextAnimation(phrase);
				boolean result = currentPlayer.throwDice(dice);
				if (result) { // add an animation
					animations.add(dice.getAnimation());
					endPhrase();
				}
			} else if (phrase == GamePhrase.Moving) {
				setPhraseTextAnimation(phrase);
				currentPlayer.decide();
			} else { // end turn
				setPhraseTextAnimation(phrase);
				endPhrase();
				int diceNo = dice.getValue();
				if (diceNo != 6) { // dice number is 6, the
									// player get 1 bonus turn
					nextTurn(); // next turn
				}
			}
		}
	}

	/*
	 * render environment
	 */
	@Override
	public void render(Graphics g) {
		List<Spot> allSpots = getAllSpots();
		for (Spot spot : allSpots) {
			spot.render(g);
		}
		for (Horsemen horse : horses) {
			horse.render(g);
		}
		dice.render(g);
		// textAnimation.render(g);
		infoPanel.render(g);
	}

	/*
	 * dispose environment
	 */
	public void dispose() {
		spots.clear();
		spots = null;
	}
}
